Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APM] Add UI Indices runtime configuration #48079

Merged
merged 7 commits into from
Oct 29, 2019

Conversation

ogupte
Copy link
Contributor

@ogupte ogupte commented Oct 14, 2019

Closes #45247
Adds UI Indices runtime configuration

  • index configuration in settings page
  • defaults to kibana.yml configuration values

Screen Shot 2019-10-24 at 10 28 44 PM

@elasticmachine
Copy link
Contributor

💔 Build Failed

@formgeist
Copy link
Contributor

@ogupte Would it be possible to rename all things "UI indices" to simply "Indices". So in the sidenav and page title.

@@ -44,7 +44,7 @@ export const apm: LegacyPluginInitializer = kibana => {
// TODO: rename to apm_oss.indexPatternTitle in 7.0 (breaking change)
apmIndexPatternTitle: config.get('apm_oss.indexPattern'),
apmServiceMapEnabled: config.get('xpack.apm.serviceMapEnabled'),
apmTransactionIndices: config.get('apm_oss.transactionIndices')
apmTransactionIndices: config.get('apm_oss.transactionIndices') // TODO address this!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the problem that injectDefaultVars has to return synchronously?
Or could you fetch the saved object here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do believe it has to be synchronous, but regardless: the issue here is that the value of apm_oss.transactionIndices could have changed via the UI Indices settings UI since it was injected here during plugin startup. So I'll have to make an API call for it whenever the client requires this index.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh yes ofc. Instead of making a separate endpoint for this, can you call the existing endpoint that exposes the ui indices?

const [uiIndices, setUiIndices] = useState<{ [key: string]: string }>({});
const [isSaving, setIsSaving] = useState(false);

const callApmApiFromHook = useCallApmApi();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to say it comes from a hook? I got a little confused with that name at first.

Suggested change
const callApmApiFromHook = useCallApmApi();
const callApmApi = useCallApmApi();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming conflicts with the function argument to useFetcher.

Copy link
Member

@sorenlouv sorenlouv Oct 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using calApmApi from the hook everywhere?

  const callApmApi = useCallApmApi();

  const { data = [], status, refetch } = useFetcher(
   () => callApmApi({ pathname: `/api/apm/settings/ui-indices` }),
    [callApmApi]
  );

I think it's confusing having two different instances of it.

<EuiSpacer />
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButton onClick={refetch}>Cancel</EuiButton>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why refetch on Cancel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancel behavior resets the values to whatever was previously saved, which can be accomplished by calling refetch. Alternatively i could add more to the component state to store the initial values, and set that when cancel is clicked.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, makes sense. Seems simpler to just refetch 👍

const { data = [], refetch } = useFetcher(
callApmApi => callApmApi({ pathname: `/api/apm/settings/ui-indices` }),
[],
{ preservePreviousData: false }
Copy link
Member

@sorenlouv sorenlouv Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why preservePreviousData: false?

Comment on lines 26 to 34
{ configuration: 'apm_oss.sourcemapIndices', label: 'Sourcemap Indices' },
{ configuration: 'apm_oss.errorIndices', label: 'Error Indices' },
{ configuration: 'apm_oss.onboardingIndices', label: 'Onboarding Indices' },
{ configuration: 'apm_oss.spanIndices', label: 'Span Indices' },
{ configuration: 'apm_oss.transactionIndices', label: 'Transaction Indices' },
{ configuration: 'apm_oss.metricsIndices', label: 'Metrics Indices' },
{
configuration: 'apm_oss.apmAgentConfigurationIndex',
label: 'Agent Configuration Index'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these need translations?

const [uiIndices, setUiIndices] = useState<{ [key: string]: string }>({});
const [isSaving, setIsSaving] = useState(false);

const callApmApiFromHook = useCallApmApi();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming conflicts with the function argument to useFetcher.

);

useEffect(() => {
setUiIndices(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it's generally more robust to initially create an empty object for the form values, and then merge them synchronously inside of the render function. This explicitly prioritizes user changes over network data, and can help eliminate situations where the user will lose data due to a race condition

<EuiSpacer size="m" />
<EuiText size="s" grow={false}>
<p>
The APM UI uses index patterns to query your APM indices. If
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translation?

import { APMClient } from '../../../../services/rest/createCallApmApi';

const INDICES = [
{ configuration: 'apm_oss.sourcemapIndices', label: 'Sourcemap Indices' },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Further down, I thought configuration was an object, but it is a string, so maybe something like configurationName would be helpful.

};
}

function flatValues<T = any>(obj: Record<string, any>, deepValues = []): T[] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a bit of a hard time understanding what this function does. I guess it's getting all the values from apm_oss? Would it be better to just explicitly define that here? Seems like deepValues is not being used either? e.g.:

return Object.values(apmIndices.apm_oss);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the purpose of flatValues as opposed to Object.values(apmIndices.apm_oss); is to keep getApmIndicesList generic. If we add a new index configuration, e.g. xpack.apm.ui.someNewIndex, then it gets included in getApmIndicesList without having to change that function. I open to changing this, but that was my original motivation.

Comment on lines 12 to 24
const APM_UI_INDICES = [
'apm_oss.sourcemapIndices',
'apm_oss.errorIndices',
'apm_oss.onboardingIndices',
'apm_oss.spanIndices',
'apm_oss.transactionIndices',
'apm_oss.metricsIndices',
'apm_oss.apmAgentConfigurationIndex'
];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps an enum would limit the amount of times we have to (re)define this?

throw err;
}

return APM_UI_INDICES.map(indexConfig => ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use this function for getApmIndices as well? Or are there subtle differences that I'm missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the difference is that getApmIndices returns the resolved static/runtime indices, while this function returns more of a breakdown of the resolution, which informs the UI how to render the settings page. i.e. getApmIndices could be derived from the result of this function, not the other way around.

const savedObjectsClient = getSavedObjectsClient(server, 'data');
try {
await savedObjectsClient.create('apm-ui-indices', uiIndicesSavedObject, {
id: 'apm-ui-indices',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen this a couple of times now as well, should we use a constant for this?

overwrite: true
});
} catch (err) {
server.log('error', err);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity, should we always intercept server errors and log them explicitly, or does that happen automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your right, it's not necessary here. I'll remove these.

@elasticmachine
Copy link
Contributor

💔 Build Failed

@ogupte ogupte force-pushed the apm-45247-runtime-ui-index-settings branch from ef64486 to 5e191ed Compare October 19, 2019 06:40
@ogupte ogupte marked this pull request as ready for review October 19, 2019 06:40
@ogupte ogupte requested a review from a team as a code owner October 19, 2019 06:40
@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

export async function getApmIndicesList(server: Server) {
const apmIndices = await getApmIndices(server);
const set = new Set<string>(Object.values(apmIndices.apm_oss)); // ensure unique values
return Array.from(set);
Copy link
Member

@sorenlouv sorenlouv Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following this. Why would getApmIndices not return unique values? Or rather: is it important whether they are unique?

Copy link
Member

@sorenlouv sorenlouv Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, now I see. This is specifically for addFilterForLegacyData? I found getApmIndicesList a little confusing. Perhaps you can call Object.values(await getApmIndices(server)) from getParamsForSearchRequest and delete getApmIndicesList because I don't see the necessity for removing duplicates.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is both getApmIndicesList and listUiIndices. That seems a little ambiguous.

try {
const apmUiIndicesSavedObject = await getApmUiIndicesSavedObject(server);
const apmUiIndicesConfig = getApmUiIndicesConfig(server.config());
return merge({}, apmUiIndicesConfig, apmUiIndicesSavedObject);
Copy link
Member

@sorenlouv sorenlouv Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the nice thing about having identical structures in the config and saved object. It's straightforward to merge them 👍

const APM_UI_INDICES_SAVED_OBJECT_ID = 'apm-ui-indices';

export async function getApmUiIndicesSavedObject(server: Server) {
const savedObjectsClient = getSavedObjectsClient(server, 'data');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget: did we settle on whether runtime index configuration should be per user, or across all users? And whether anyone should be able to change it or only admins?

'apmTransactionIndices'
);
const callApmApi: APMClient = createCallApmApi(http);
const indexPatternName = await callApmApi({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const indexPatternName = await callApmApi({
const transactionIndices = await callApmApi({

const { server } = core.http;
const setup = await setupRequest(req);
const { indexConfigurationName } = path;
return await getUiIndex({ setup, server, indexConfigurationName });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of filtering on the backend, what about simply returning all config options:

return await getApmIndices(server);

It's not a big response, so the savings ar negligible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way you can get rid of get_ui_index.ts

@@ -0,0 +1,90 @@
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this file be located in lib/settings/ui_indices?

(acc, key) => set(acc, key, uiIndices[key]),
{ apm_oss: {} }
);
return await storeApmUiIndicesSavedObject(server, uiIndicesSavedObject);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the reducer above necessary? Currently only keys in apm_oss is supported anyway. So wouldn't this work?

Suggested change
return await storeApmUiIndicesSavedObject(server, uiIndicesSavedObject);
return await storeApmUiIndicesSavedObject(server, uiIndices);

}

interface ApmUiIndicesSavedObject {
[key: string]: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only necessary to satisfy the savedObjectsClient.get interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, it satisfies the SavedObjectAttributes type

@@ -31,12 +32,17 @@ export async function setupRequest(req: Legacy.Request) {
const query = (req.query as unknown) as APMRequestQuery;
const { server } = req;
const config = server.config();
const [uiFiltersES, indices] = await Promise.all([
decodeUiFilters(server, query.uiFilters),
getApmIndices(server)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently getApmIndices is called twice for each request. First to get the list of APM indices, and afterwards to verify whether it is an APM index that was queries or not.

What do you think about caching getApmIndices for a short amount of time, eg. 5 seconds? That way if we make several concurrent requests hopefully they won't all have to call getApmIndices. At worst they'll only do it once per request.

);
}
return indexConfigurationValue;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned elsewhere I don't think this method is necessary.

"apm_oss": {
"properties": {
"sourcemapIndices": {
"type": "text"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

text is mostly used for full-text searches. It will go through the analyzer, will be tokenized and such.
Unless we want that we should use keyword

@@ -0,0 +1,61 @@
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about moving this to x-pack/legacy/plugins/apm/server/routes/settings/ui_indices.ts.
Similarly x-pack/legacy/plugins/apm/server/routes/settings.ts should be moved to x-pack/legacy/plugins/apm/server/routes/settings/agent_configuration.ts

@ogupte ogupte force-pushed the apm-45247-runtime-ui-index-settings branch from 84bf62b to 6262e63 Compare October 24, 2019 08:24
@elasticmachine
Copy link
Contributor

💔 Build Failed

@ogupte ogupte force-pushed the apm-45247-runtime-ui-index-settings branch from 6262e63 to d0421ac Compare October 25, 2019 05:56
@elasticmachine
Copy link
Contributor

💔 Build Failed

}
"apm_oss.sourcemapIndices": {
"type": "keyword"
},
Copy link
Member

@sorenlouv sorenlouv Oct 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good move to flatten the option keys 👍

}: {
callApmApi: APMClient;
uiIndices: StringMap<string>;
apmIndices: StringMap<string>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this everywhere. Tedious work but much more consistent now 👍

client,
indices: { apm_oss }
} = setup;
const { start, end, uiFiltersES, client, indices } = setup;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to be back on a single line 👍

Copy link
Member

@sorenlouv sorenlouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

Couple of TODOs

  • decide whether it should be per person or for all
  • decide whether specific permissions are required
  • possible caching so each request doesn't fetch the saved object twice

I'm okay with merging as-is and then tackle the above questions in follow-up PRs

config.get<string>('apm_oss.transactionIndices')
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices']
Copy link
Member

@sorenlouv sorenlouv Oct 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each config options can contain a comma separated list of several options. This is mostly used for CCS like apm-*, *:apm-*:

index: [
  'apm-*-transaction-*, *:apm-*-transaction-*',
  'apm-*-span-*, *:apm-*-span-*',
  'apm-*-error-*, *:apm-*-error-*'
]

Can you make sure that queries work with comma-separated strings?

- index configuration in settings page
- defaults to kibana.yml configuration values
.apm-agent-configuration index which caused failures in those APIs
@ogupte ogupte force-pushed the apm-45247-runtime-ui-index-settings branch from d0421ac to 0409cd2 Compare October 28, 2019 23:10
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@ogupte ogupte merged commit c3996e2 into elastic:master Oct 29, 2019
ogupte added a commit to ogupte/kibana that referenced this pull request Oct 29, 2019
* Add UI Indices runtime configuration
- index configuration in settings page
- defaults to kibana.yml configuration values

* fix tests

* Code review feedback and cleanup

* fix i18n

* Code review feedback

* Address code review feedback.

* Fixes bug where legacy data filter was including the
.apm-agent-configuration index which caused failures in those APIs
ogupte added a commit that referenced this pull request Oct 29, 2019
* Add UI Indices runtime configuration
- index configuration in settings page
- defaults to kibana.yml configuration values

* fix tests

* Code review feedback and cleanup

* fix i18n

* Code review feedback

* Address code review feedback.

* Fixes bug where legacy data filter was including the
.apm-agent-configuration index which caused failures in those APIs
@sorenlouv sorenlouv mentioned this pull request Oct 29, 2019
@sorenlouv
Copy link
Member

Great to see this merged. Good job @ogupte ! Will you create the follow-up issues regarding permission model?

@smith
Copy link
Contributor

smith commented Jan 24, 2020

Test:
Chrome: ✅
Firefox: ✅
IE11: ✅
Safari: ✅

@smith smith added the apm:test-plan-done Pull request that was successfully tested during the test plan label Jan 24, 2020
@smith smith removed their assignment Jan 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
apm:test-plan-done Pull request that was successfully tested during the test plan release_note:enhancement v7.6.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[APM] Implementation of runtime UI index configuration
6 participants